/*
 * Created on 10-sep-2004
 *
 */
package gurulab.merlin.core.router;

import java.util.Date;

import gurulab.maya.common.MessageType;
import gurulab.merlin.core.CantHandException;
import gurulab.merlin.core.RuleSyntaxErrorException;
import gurulab.merlin.core.envelope.Envelope;
import gurulab.merlin.core.envelope.EnvelopeHandler;
import gurulab.merlin.core.router.transformation.AbstractEnvelopeTransformer;
import gurulab.util.Commander;

import org.apache.log4j.Logger;

/**
 * @author cobach
 *
 */
public class Router implements EnvelopeHandler{
    Logger logger = Logger.getLogger(Router.class);
    private Route FAULT_ROUTE = new Route("internalFaultRoute","internal");
    
    private RoutingTableHandler routingTable;
    private EnvelopeHandler handler;
    private EnvelopeTransformer transformer;
    private String xmlTransformationFilePath;
    
    public Router(EnvelopeHandler h, Commander commander, String xmlRoutingTableFilePath, String xmlTransformationFilePath) throws RuleSyntaxErrorException {
        handler = h;
        routingTable = new RoutingTableHandler(commander.getValue("router.routingTable"));
        this.xmlTransformationFilePath = xmlTransformationFilePath;
    }
    
    public Object handEnvelope(Envelope envelope) throws CantHandException {
        String msg = "";
        try {
            envelope = preRouteEnvelope(envelope);
             
            //Tranformar
            logger.debug("Transformacion del envelope");            
            if(envelope.getDestinationRoute()!=null){
            	transformer = new AbstractEnvelopeTransformer(xmlTransformationFilePath).getTransformerByPeer(envelope.getDestinationRoute().getPeer());
            }
            if(transformer==null && envelope.getOriginRoute()!=null){
            	transformer = new AbstractEnvelopeTransformer(xmlTransformationFilePath).getTransformerByPeer(envelope.getOriginRoute().getPeer());
            }
            
            if (transformer!=null) {
                transformer.transform(envelope);
                logger.debug("Transformacion realizada - "+transformer.getClass());
            }else{
            	logger.info("No se realizo la transformacion -- transformer = null");
            }
            
            envelope.setDestinationRoute(routeEnvelope(envelope));
            logger.debug("Mensaje enrutado a:'" + envelope.getDestinationRoute().toString() + "'");

            
           if(envelope.getComment().equals(MessageType.SMS_PURE_MT)){
            	//Se realiza la transformacion para el destino
                if(envelope.getDestinationRoute()!=null){
                	transformer = new AbstractEnvelopeTransformer(xmlTransformationFilePath).getTransformerByPeer(envelope.getDestinationRoute().getPeer());
                }
                if (transformer!=null) {
                    transformer.transform(envelope);
                	logger.debug("Se realiza la transformacion para el destino del mensaje... "+envelope.getDestinationRoute().getPeer());
                }else{
                	logger.debug("No se realizo la transformacion -- transformer = null");
                }
            }            
            
            transformer = null;
            logger.debug("Limpiando la referencia del Transform");
            
            getHandler().handEnvelope(envelope);
        } catch (CantHandException e) {
            logger.error("Error de enturamiento: " + e);
            handleError(envelope, e, "Handling Error");
        } catch (Throwable e) {
            logger.error("Error Interno: " + e);
            e.printStackTrace();
            handleError(envelope, new CantHandException("Internal Error: " + e), "Internal Error");
        } finally{
            
        }
        return envelope;
    }
    
    public Envelope preRouteEnvelope(Envelope envelope) throws CantHandException {
        return (envelope);
    }
    
    public Route routeEnvelope(Envelope envelope) throws CantHandException {
    	
    	logger.debug("Mensaje recibido por enrutador: " + envelope.toString());
        try {
            Date begin = new Date();
            Route route = routingTable.firstRoute2Comply(envelope);
            logger.debug(route.toString());
            envelope.setRoutingTime(new Date().getTime() - begin.getTime());
            return route;            
        } catch (Exception e) {
            throw new CantHandException("reason: " + e);
        }
    }
    
    public EnvelopeHandler getHandler() {
        return handler;
    }
    
    public String getVersion() {
        return ("Merlin Router v2.1");
    }
    
    public void handleError(Envelope envelope, Throwable track, String reason) {
        envelope.setDestinationRoute(FAULT_ROUTE);
        envelope.setComment(reason);
        envelope.setThrowable(track);
        try {
            getHandler().handEnvelope(envelope);
        } catch (Exception e) {
            logger.error("Could not store envelope on FAULT QUEUE!: " + envelope);
        }
    }
    
    /**
     * Ejecuta un procesamiento previo a la ejecucin de la accin sobre el objeto envelope.
     *
     * @param envelope
     * @return un objeto referencia del preprocesamiento.
     * @throws Exception
     */
    public Object preProcesingAction(Envelope envelope) throws Exception {
        return null;
    }
    
    /**
     * Ejecuta un procesamiento posterior a la ejecucin de la accin sobre el objeto envelope.
     * llamada sincrona
     * @param envelope
     * @return un objeto referencia del postprocesamiento.
     * @throws Exception
     */
    public Object postProcesingAction(Envelope envelope) throws Exception {
        return null;
    }
    
    //llamada asincrona
    public Object postProcesingAction(int dataID, int status) throws Exception {
        return null;
    }
    
    /**
     * Efecta una especie de rollback del preprocesamiento realizado, valindose del objeto
     * referencia.
     *
     * @param actionReference
     *            Objeto referencia del preprocesamiento realizado.
     * @throws Exception
     */
    public void preProcesingActionCompensator(Object actionReference) throws Exception {
        
    }
    
    /**
     * Efecta una especie de rollback del postprocesamiento realizado, valindose del objeto
     * referencia.
     *
     * @param actionReference
     *            Objeto referencia del postprocesamiento realizado.
     * @throws Exception
     */
    public void postProcesingActionCompensator(Object actionReference) throws Exception {
        
    }
    
    /*
     * (sin Javadoc)
     *
     * @see gurulab.merlin.core.EnvelopeHandler#retrieveEnvelope(java.lang.Object)
     */
    public void retrieveEnvelope(Object EnvelopeReference) throws CantHandException {
    }
    
    /**
     * Hace persistente el procesamientos de un objeto Envelope.
     *
     * @param EnvelopeReference.
     *            Referencia al Envelope que fue procesado.
     * @throws CantHandException
     */
    public void commitHandEnvelope(Object EnvelopeReference) throws CantHandException {
    }
    
    /**
     * Hace persistente el precesamientos de un objeto Envelope.
     *
     * @param actionReference
     *            Objeto referencia del preprocesamiento realizado.
     * @throws CantHandException
     */
    public void commitPreProcesingAction(Object actionReference) throws CantHandException {
    }
    
    /**
     * Hace persistente el postcesamientos de un objeto Envelope.
     *
     * @param actionReference
     *            Objeto referencia del postprocesamiento realizado.
     * @throws CantHandException
     */
    public void commitPostProcesingAction(Object actionReference) throws CantHandException {
    }
    
    
    
}